#include <ut/UnitTest.h>
#include <illib/Log.h>
#include <illib/BasicLinkedList.h>
#include <sstream>
#include <list>
#include <string.h>

using namespace illib;
using namespace std;

namespace illib_ut{
	void testBasicListAgainstExpected(BasicLinkedList::List& list, BasicLinkedList::Node elements[], const char* indexes) {
		size_t length = strlen(indexes);
		char * reversed = new char[length+1];
		reversed[length] = '\0';
		for(size_t i = 0; i < length; i++) reversed[i] = indexes[(length-1)-i];

		//Log::debug("Testing list forwards %s \n", indexes);
		//list.debug();
		{
			stringstream stream(indexes);
			size_t index;
			size_t i = 0;
			BasicLinkedList::Node * head = 0;
			BasicLinkedList::Node * tail = 0;
			while (!stream.eof()){
				stream >> index;
				ut::test(AT(), EX(list.get(i) == &elements[index]), TN(BasicLinkedList::List::get));
				if(head == 0) head = &elements[index];
				tail = &elements[index];
				i++;
			}
			ut::test(AT(), EX(head == list.head()), TN(BasicLinkedList::List::head));
			ut::test(AT(), EX(tail == list.tail()), TN(BasicLinkedList::List::tail));
		}
		{
			stringstream stream(indexes);
			size_t index;
			BasicLinkedList::Iterator iterator(list);
			while(iterator.current()){
				stream >> index;
				ut::test(AT(), EX(iterator.current() == &elements[index]), TN(BasicLinkedList::Iterator::current));
				iterator.next();
			}
		}
		{
			stringstream stream(reversed);
			size_t index;
			BasicLinkedList::Iterator iterator(list, true);
			while(iterator.current()){
				stream >> index;
				ut::test(AT(), EX(iterator.current() == &elements[index]), TN(BasicLinkedList::Iterator::current));
				iterator.prev();
			}
		}
		{
			stringstream stream(indexes);
			size_t index;
			BasicLinkedList::Iterator iterator(list);
			while (!stream.eof()){
				stream >> index;
				//Log::debug("Expected %p, got %p\n", &elements[index], iterator.current());
				ut::test(AT(), EX(iterator.current() == &elements[index]),TN(BasicLinkedList::Iterator::current));
				iterator.next();
			}
		}
		{

			BasicLinkedList::Iterator iterator(list, true);
			size_t index;
			//Log::debug("Testing backwards %s\n", reversed);
			stringstream stream(reversed);
			while (!stream.eof()){
				stream >> index;
				//Log::debug("Expected %p, got %p\n", &elements[index], iterator.current());
				ut::test(AT(), EX(iterator.current() == &elements[index]),TN(BasicLinkedList::Iterator::current));
				iterator.prev();
			}
		}
		delete reversed;
	}

	void testBasicLinkedList(BasicLinkedList::List &list){
		const size_t listSize = 9; //this will break if greater than 9
		BasicLinkedList::Node elements[listSize];

		ut::test(AT(), EX(!list.length()),TN(BasicLinkedList::List::length));
		ut::test(AT(), EX(!list.remove(0)),TN(BasicLinkedList::List::remove));
		ut::test(AT(), EX(!list.add(elements[0],1)),TN(BasicLinkedList::List::add));
		ut::test(AT(), EX(!list.get(0)),TN(BasicLinkedList::List::get));
		ut::test(AT(), EX(!list.get(1)),TN(BasicLinkedList::List::get));
		ut::test(AT(), EX(!list.head()),TN(BasicLinkedList::List::head));
		ut::test(AT(), EX(!list.tail()),TN(BasicLinkedList::List::head));

		ut::test(AT(), EX(list.add(elements[0],0)),TN(BasicLinkedList::List::add));
		ut::test(AT(), EX(list.length() == 1),TN(BasicLinkedList::List::length));
		testBasicListAgainstExpected(list, elements, "0");

		ut::test(AT(), EX(list.add(elements[1],0)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "1 0");

		ut::test(AT(), EX(list.add(elements[2],1)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "1 2 0");

		ut::test(AT(), EX(!list.add(elements[3],4)),TN(BasicLinkedList::List::add));
		ut::test(AT(), EX(list.add(elements[3],3)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "1 2 0 3");

		ut::test(AT(), EX(list.add(elements[5],2)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "1 2 5 0 3");

		ut::test(AT(), EX(list.add(elements[6],0)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "6 1 2 5 0 3");

		ut::test(AT(), EX(list.add(elements[7],list.length())),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "6 1 2 5 0 3 7");

		ut::test(AT(), EX(list.add(elements[8],6)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "6 1 2 5 0 3 8 7");

		ut::test(AT(), EX(list.remove(0) == &elements[6]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 5 0 3 8 7");

		ut::test(AT(), EX(!list.remove(7)), TN(BasicLinkedList::List::remove));
		ut::test(AT(), EX(list.remove(6) == &elements[7]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 5 0 3 8");

		ut::test(AT(), EX(list.remove(3) == &elements[0]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 5 3 8");
		ut::test(AT(), EX(list.length() == 5),TN(BasicLinkedList::List::length));

		ut::test(AT(), EX(list.add(elements[0],list.length())),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "1 2 5 3 8 0");

		ut::test(AT(), EX(list.add(elements[4],0)),TN(BasicLinkedList::List::add));
		testBasicListAgainstExpected(list, elements, "4 1 2 5 3 8 0");

		ut::test(AT(), EX(list.remove(*list.get(0)) == &elements[4]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 5 3 8 0");

		ut::test(AT(), EX(list.remove(*list.get(list.length()-1)) == &elements[0]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 5 3 8");

		ut::test(AT(), EX(list.remove(*list.get(2)) == &elements[5]), TN(BasicLinkedList::List::remove));
		testBasicListAgainstExpected(list, elements, "1 2 3 8");
	}

	void BasicLinkedList_ut() {
		ut::UnitTestLogger logger(TN(illib_ut::BasicLinkedList_ut));
		BasicLinkedList::List list;
		testBasicLinkedList(list);
		list.clear();
		testBasicLinkedList(list);
	}
}

